﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace InstantSpine.Core
{
    /// <summary>
    /// A bidirectional map from K to V and
    /// from V back to K
    /// </summary>
    /// <remarks>All lookups are O(1). A BiDiMap{K,V} can
    /// also be used as a plain IDictionary{K,V}</remarks>
    /// <typeparam name="K">type of the keys (or values for backward lookups)</typeparam>
    /// <typeparam name="V">type of the values (or keys for backward lookups)</typeparam>
    public class BiDiMap<K, V> : ICollection<KeyValuePair<K, V>>, IEnumerable<KeyValuePair<K, V>>, IDictionary<K, V>
    {
        IDictionary<K, V> forwardMap;
        IDictionary<V, K> backwardMap;

        /// <summary>
        /// creates a new BiDiMap
        /// </summary>
        public BiDiMap()
        {
            forwardMap = new Dictionary<K, V>();
            backwardMap = new Dictionary<V, K>();
        }

        /// <summary>
        /// creates a new BiDiMap
        /// </summary>
        /// <param name="capacity">expected size of the map (for pre-allocation)</param>
        public BiDiMap(int capacity)
        {
            forwardMap = new Dictionary<K, V>(capacity);
            backwardMap = new Dictionary<V, K>(capacity);
        }

        #region IDictionary<T,K> Members

        /// <summary>
        /// adds a key/value-value/key-pair to the map
        /// </summary>
        /// <param name="key">key for forward lookups (value for backward)</param>
        /// <param name="value">value for forward lookups (key for backward)</param>
        public void Add(K key, V value)
        {
            forwardMap.Add(key, value);
            backwardMap.Add(value, key);
        }

        public bool ContainsKey(K key)
        {
            return forwardMap.ContainsKey(key);
        }

        public ICollection<K> Keys
        {
            get { return forwardMap.Keys; }
        }

        public bool Remove(K key)
        {
            V value = forwardMap[key]; // get key for second dict
            
            bool res = forwardMap.Remove(key);
            res &= backwardMap.Remove(value);
            
            return res;
        }

        public bool TryGetValue(K key, out V value)
        {
            return forwardMap.TryGetValue(key, out value);
        }

        public ICollection<V> Values
        {
            get { return forwardMap.Values; }
        }

        public V this[K key]
        {
            get
            {
                return forwardMap[key];
            }
            set
            {
                forwardMap[key] = value;
                backwardMap[value] = key;
            }
        }

        #endregion

        #region ICollection<KeyValuePair<T,K>> Members

        public void Add(KeyValuePair<K, V> item)
        {
            forwardMap.Add(item);
            backwardMap.Add(item.Value, item.Key);
        }

        public void Clear()
        {
            forwardMap.Clear();
            backwardMap.Clear();
        }

        public bool Contains(KeyValuePair<K, V> item)
        {
            return forwardMap.Contains(item);
        }

        public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
        {
            forwardMap.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return forwardMap.Count; }
        }

        public bool IsReadOnly
        {
            get { return forwardMap.IsReadOnly; }
        }

        public bool Remove(KeyValuePair<K, V> item)
        {
            return backwardMap.Remove(item.Value) && forwardMap.Remove(item);
        }

        #endregion

        #region IEnumerable<KeyValuePair<T,K>> Members

        public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
        {
            return forwardMap.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return forwardMap.GetEnumerator();
        }

        #endregion

        #region IDictionary<K,T> Members

        public void Add(V key, K value)
        {
            backwardMap.Add(key, value);
            forwardMap.Add(value, key);
        }

        public bool ContainsKey(V key)
        {
            return backwardMap.ContainsKey(key);
        }

        public bool Remove(V key)
        {
            K val = backwardMap[key];
            return forwardMap.Remove(val) && backwardMap.Remove(key);
        }

        public bool TryGetValue(V key, out K value)
        {
            return backwardMap.TryGetValue(key, out value);
        }

        public K this[V key]
        {
            get
            {
                return backwardMap[key];
            }
            set
            {
                backwardMap[key] = value;
                forwardMap[value] = key;
            }
        }

        #endregion

        #region ICollection<KeyValuePair<K,T>> Members

        public void Add(KeyValuePair<V, K> item)
        {
            backwardMap.Add(item);
            forwardMap.Add(item.Value, item.Key);
        }

        public bool Contains(KeyValuePair<V, K> item)
        {
            return backwardMap.Contains(item);
        }

        public void CopyTo(KeyValuePair<V, K>[] array, int arrayIndex)
        {
            backwardMap.CopyTo(array, arrayIndex);
        }

        public bool Remove(KeyValuePair<V, K> item)
        {
            forwardMap.Remove(item.Value);
            return backwardMap.Remove(item);
        }

        #endregion
    }
}
